﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using JqlEditor.Properties;

namespace JqlEditor
{
    public partial class FrmMain : Form
    {
        private readonly static Point EmptyLocation = new Point(-1, -1);

        private ChessmanColor ChessmanColor { get; set; }
        private Bitmap ChessmanImage { get; set; }
        private string OpenedFileName;
        private bool LayoutChanged { get; set; }
        private JqlInfo JqlInfo { get; set; }
        private Point FocusedLocation { get; set; }
        private Point SelectedLocation { get; set; }
        private Point PrevSelectedLocation { get; set; }
        private Stack<JqlChessmanSwap> UndoList { get; set; }
        private Stack<JqlChessmanSwap> RedoList { get; set; }

        public FrmMain()
        {
            InitializeComponent();

            InitializeBoard();
            InitializeElements();
        }

        private void ResizeBoard()
        {
            pbxChessboard.Location = new Point(0, 0);
            ClientSize = new Size(pbxChessboard.Width, pbxChessboard.Height);
        }
        private void InitializeBoard()
        {
            ResizeBoard();
            ChessmanColor = JqlConfigurationManager.Manager.DefaultChessmanColor;
            ChessmanImage = GetChessmanImage(ChessmanColor);
            SetColorRadioSelected(ChessmanColor.ToString());
        }
        private void InitializeElements()
        {
            OpenedFileName = String.Empty;
            LayoutChanged = false;
            JqlInfo = JqlInfo.Default;

            FocusedLocation = EmptyLocation;
            SelectedLocation = EmptyLocation;
            PrevSelectedLocation = EmptyLocation;

            UndoList = new Stack<JqlChessmanSwap>();
            RedoList = new Stack<JqlChessmanSwap>();
        }

        private Bitmap GetChessmanImage(ChessmanColor color)
        {
            switch (color)
            {
                case ChessmanColor.Orange: return Resources.Orange;
                case ChessmanColor.Purple: return Resources.Purple;
                case ChessmanColor.Green: return Resources.Green;
                case ChessmanColor.Blue: return Resources.Blue;
                default: return default(Bitmap);
            }
        }
        private Point GetPositionInLayout(Point mouseLocation)
        {
            int horiSpan = JqlConsts.ChessmanWidth + JqlConsts.HoriMargin;
            int vertSpan = JqlConsts.ChessmanHeight + JqlConsts.VertMargin;

            int x = (mouseLocation.X - JqlConsts.OffsetLeft) / horiSpan;
            int y = (mouseLocation.Y - JqlConsts.OffsetTop) / vertSpan;

            return new Point(x, y);
        }
        private Rectangle GetChessmanRectangleInBoard(Point layoutPosition)
        {
            layoutPosition.X = layoutPosition.X * (JqlConsts.ChessmanWidth + JqlConsts.HoriMargin) + JqlConsts.OffsetLeft;
            layoutPosition.Y = layoutPosition.Y * (JqlConsts.ChessmanHeight + JqlConsts.VertMargin) + JqlConsts.OffsetTop;

            return new Rectangle(layoutPosition.X, layoutPosition.Y, JqlConsts.ChessmanWidth, JqlConsts.ChessmanHeight);
        }
        private Rectangle GetFrameRectangleInBoard(Point layoutPosition)
        {
            layoutPosition.X = layoutPosition.X * (JqlConsts.ChessmanWidth + JqlConsts.HoriMargin) + JqlConsts.OffsetLeft;
            layoutPosition.Y = layoutPosition.Y * (JqlConsts.ChessmanHeight + JqlConsts.VertMargin) + JqlConsts.OffsetTop;
            layoutPosition.Offset(-10, -10);
            return new Rectangle(layoutPosition.X, layoutPosition.Y, JqlConsts.FrameWidth, JqlConsts.FrameHeight);
        }
        private bool IsMouseOverChessman(Point mouseLocation, out Point positionInLayout)
        {
            positionInLayout = GetPositionInLayout(mouseLocation);

            SpecialPosition position = JqlChessmanCollection.CheckPosition(positionInLayout.Y, positionInLayout.X);
            if (position == SpecialPosition.None || position == SpecialPosition.Bunker)
            {
                return false;
            }

            Rectangle rect = GetChessmanRectangleInBoard(positionInLayout);
            return rect.Contains(mouseLocation);
        }
        private void PlayClickSound()
        {
            SoundPlayer.Play(Path.Combine(Application.StartupPath, "Select.wav"), false);
        }
        private void SetCaption(string filePath)
        {
            if (File.Exists(filePath))
            {
                Text = Path.GetFileName(filePath);
            }
            else
            {
                Text = filePath;
            }
        }
        private void OpenJqlFile()
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                ofd.Title = "打开布局文件";
                ofd.InitialDirectory = JqlConfigurationManager.Manager.DefaultOpenFileDirectory;
                ofd.Filter = JqlConsts.JqlFileFilter;

                if (ofd.ShowDialog(this) == DialogResult.OK)
                {
                    OpenedFileName = ofd.FileName;
                    LayoutChanged = false;
                    SetCaption(ofd.FileName);

                    JqlInfo = JqlInfo.LoadFrom(OpenedFileName);
                    pbxChessboard.Invalidate();
                }
            }
        }
        private DialogResult SaveJqlFile(ref string fileName)
        {
            DialogResult result = DialogResult.OK;

            if (string.IsNullOrEmpty(fileName))
            {
                using (SaveFileDialog sfd = new SaveFileDialog())
                {
                    sfd.Title = "保存布局文件";
                    sfd.InitialDirectory = JqlConfigurationManager.Manager.DefaultSaveAsFileDirectory;
                    sfd.Filter = JqlConsts.JqlFileFilter;

                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        fileName = sfd.FileName;
                        LayoutChanged = false;
                        UndoList.Clear();
                        RedoList.Clear();
                    }
                    else
                    {
                        result = DialogResult.Cancel;
                    }
                }
            }

            if (result == DialogResult.OK)
            {
                SetCaption(fileName);
                JqlInfo.Save(fileName);
            }

            return result;
        }
        private void CreateNewRandomLayout()
        {
            OpenedFileName = String.Empty;
            LayoutChanged = false;
            UndoList.Clear();
            RedoList.Clear();

            JqlInfo = JqlInfo.FromRandom();
            SetCaption("QQ 军棋布局器");
            pbxChessboard.Invalidate();
        }
        private Image BuildBoardImage()
        {
            int horiSpan = JqlConsts.ChessmanWidth + JqlConsts.HoriMargin;
            int vertSpan = JqlConsts.ChessmanHeight + JqlConsts.VertMargin;

            var brdImage = new Bitmap(Resources.JqlBoard.Width, Resources.JqlBoard.Height);

            using (var g = Graphics.FromImage(brdImage))
            {
                g.DrawImage(Resources.JqlBoard, new RectangleF(0, 0, brdImage.Width, brdImage.Height));

                for (int r = 0; r < JqlConsts.LayoutRowCount; r++)
                {
                    for (int c = 0; c < JqlConsts.LayoutColCount; c++)
                    {
                        if (JqlInfo.ChessmanCollection[r, c].Visible)
                        {
                            int x = JqlConsts.OffsetLeft + c * horiSpan;
                            int y = JqlConsts.OffsetTop + r * vertSpan;
                            var imageRect = new Rectangle(x, y, JqlConsts.ChessmanWidth, JqlConsts.ChessmanHeight);
                            g.DrawImage(JqlInfo.ChessmanCollection[r, c].ToImage(ChessmanImage), imageRect);
                        }
                    }
                }
            }

            return brdImage;
        }
        private void CopyImageToClipboard()
        {
            var image = BuildBoardImage();
            Clipboard.SetData(DataFormats.Bitmap, image);
            MessageBox.Show("当前布局已经复制成功");
        }
        private void ChangeChessmanColor(string color)
        {
            ChessmanColor = (ChessmanColor)Enum.Parse(typeof(ChessmanColor), color);
            JqlConfigurationManager.Manager.DefaultChessmanColor = ChessmanColor;
            ChessmanImage = GetChessmanImage(ChessmanColor);
            pbxChessboard.Invalidate();
        }
        private void Undo()
        {
            if (UndoList.Count > 0)
            {
                JqlChessmanSwap swap = UndoList.Pop();
                if (swap.SwapType == ChessmanSwapType.FlipSwap)
                {
                    JqlInfo.ChessmanCollection.Flip();
                }
                else
                {
                    JqlInfo.ChessmanCollection.Swap(swap.SrcLoc, swap.DstLoc);
                }
                RedoList.Push(swap);
                pbxChessboard.Invalidate();
            }
        }
        private void Redo()
        {
            if (RedoList.Count > 0)
            {
                JqlChessmanSwap swap = RedoList.Pop();
                if (swap.SwapType == ChessmanSwapType.FlipSwap)
                {
                    JqlInfo.ChessmanCollection.Flip();
                }
                else
                {
                    JqlInfo.ChessmanCollection.Swap(swap.SrcLoc, swap.DstLoc);
                }
                UndoList.Push(swap);
                pbxChessboard.Invalidate();
            }
        }
        private void SetColorRadioSelected(string chessmanColor)
        {
            mnuOrange.Checked = String.Equals(chessmanColor, "Orange", StringComparison.OrdinalIgnoreCase);
            mnuPurple.Checked = String.Equals(chessmanColor, "Purple", StringComparison.OrdinalIgnoreCase);
            mnuGreen.Checked = String.Equals(chessmanColor, "Green", StringComparison.OrdinalIgnoreCase);
            mnuBlue.Checked = String.Equals(chessmanColor, "Blue", StringComparison.OrdinalIgnoreCase);
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(Program.FilePath))
            {
                OpenedFileName = Program.FilePath;
                LayoutChanged = false;
                SetCaption(OpenedFileName);

                JqlInfo = JqlInfo.LoadFrom(OpenedFileName);
                pbxChessboard.Invalidate();
            }
        }

        private void pbxChessboard_Paint(object sender, PaintEventArgs e)
        {
            var chessmanColl = JqlInfo.ChessmanCollection;
            int horiSpan = JqlConsts.ChessmanWidth + JqlConsts.HoriMargin;
            int vertSpan = JqlConsts.ChessmanHeight + JqlConsts.VertMargin;

            try
            {
                for (int r = 0; r < JqlConsts.LayoutRowCount; r++)
                {
                    for (int c = 0; c < JqlConsts.LayoutColCount; c++)
                    {
                        if (chessmanColl[r, c].Visible)
                        {
                            int x = JqlConsts.OffsetLeft + c * horiSpan;
                            int y = JqlConsts.OffsetTop + r * vertSpan;
                            var imageRect = new Rectangle(x, y, JqlConsts.ChessmanWidth, JqlConsts.ChessmanHeight);
                            e.Graphics.DrawImage(chessmanColl[r, c].ToImage(ChessmanImage), imageRect);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                throw;
            }

            if (FocusedLocation != EmptyLocation && FocusedLocation != SelectedLocation)
            {
                Rectangle rect = GetFrameRectangleInBoard(FocusedLocation);
                e.Graphics.DrawImage(Resources.Focus, rect);
            }

            if (SelectedLocation != EmptyLocation)
            {
                if (PrevSelectedLocation != SelectedLocation)
                {
                    Rectangle rect = GetFrameRectangleInBoard(SelectedLocation);
                    e.Graphics.DrawImage(Resources.Selected, rect);
                }
            }
        }
        private void pbxChessboard_MouseMove(object sender, MouseEventArgs e)
        {
            Point positionInLayout;

            if (IsMouseOverChessman(e.Location, out positionInLayout))
            {
                Cursor = Cursors.Hand;
                FocusedLocation = new Point(positionInLayout.X, positionInLayout.Y);
            }
            else
            {
                Cursor = Cursors.Default;
                FocusedLocation = EmptyLocation;
            }

            pbxChessboard.Invalidate();
        }
        private void pbxChessboard_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;

            Point positionInLayout;
            if (!IsMouseOverChessman(e.Location, out positionInLayout)) return;

            PrevSelectedLocation = SelectedLocation;
            SelectedLocation = new Point(positionInLayout.X, positionInLayout.Y);

            if (SelectedLocation != EmptyLocation && PrevSelectedLocation != EmptyLocation)
            {
                if (PrevSelectedLocation == SelectedLocation)
                {
                    SelectedLocation = EmptyLocation;
                    PlayClickSound();
                }
                else
                {
                    // 交换位置
                    bool success = JqlInfo.ChessmanCollection.Swap(PrevSelectedLocation, SelectedLocation);
                    if (success)
                    {
                        PlayClickSound();
                        LayoutChanged = true;

                        UndoList.Push(new JqlChessmanSwap(PrevSelectedLocation, SelectedLocation));
                        RedoList.Clear();
                    }
                    PrevSelectedLocation = SelectedLocation = EmptyLocation;
                }
            }
            else
            {
                PlayClickSound();
            }

            pbxChessboard.Invalidate();
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            bool result = base.ProcessCmdKey(ref msg, keyData);
            
            // 按下 Ctrl + Z 取消上一步操作
            if (keyData == (Keys.Control | Keys.Z))
            {
                Undo();
            }

            // 按下 Ctrl + Shift + Z 或者 Ctrl + Y 重做上一次取消的操作
            if (keyData == (Keys.Control | Keys.Shift | Keys.Z) || keyData == (Keys.Control | Keys.Y))
            {
                Redo();
            }

            // 将棋盘布局复制为图像放到内存中
            if (keyData == (Keys.Control | Keys.C))
            {
                CopyImageToClipboard();
            }

            return result;
        }

        private void FrmMain_DragDrop(object sender, DragEventArgs e)
        {
            var file = (e.Data.GetData(DataFormats.FileDrop) as string[])[0];
            if (!String.Equals(Path.GetExtension(file), ".jql", StringComparison.OrdinalIgnoreCase))
            {
                MessageBox.Show("请拖拽一个正确的军棋布局文件", "布局文件读取错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            else
            {
                FileInfo fileInfo = new FileInfo(file);
                InitializeElements();
                OpenedFileName = file;
                LayoutChanged = false;
                SetCaption(OpenedFileName);

                JqlInfo = JqlInfo.LoadFrom(OpenedFileName);
                pbxChessboard.Invalidate();
            }
        }
        private void FrmMain_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
            {
                e.Effect = DragDropEffects.All;
            }
        }

        private void mnuOpen_Click(object sender, EventArgs e)
        {
            if (LayoutChanged)
            {
                DialogResult dlgRst = MessageBox.Show("是否保存当前修改的布局？", "布局保存", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dlgRst == DialogResult.Cancel) return;
                if (dlgRst == DialogResult.Yes)
                {
                    if (SaveJqlFile(ref OpenedFileName) == DialogResult.OK)
                    {
                        MessageBox.Show("保存成功", "保存文件", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }

            OpenJqlFile();
        }
        private void mnuSave_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(OpenedFileName))
            {
                using (SaveFileDialog sfd = new SaveFileDialog())
                {
                    sfd.InitialDirectory = JqlConfigurationManager.Manager.DefaultOpenFileDirectory;
                    sfd.Filter = "军旗布局文件(*.jql)|*.jql";

                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        JqlInfo.Save(sfd.FileName);
                        OpenedFileName = sfd.FileName;
                        LayoutChanged = false;
                        UndoList.Clear();
                        RedoList.Clear();
                        MessageBox.Show("保存成功", "保存文件", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }
            else
            {
                JqlInfo.Save(OpenedFileName);
                MessageBox.Show("保存成功", "保存文件", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        private void mnuSaveAs_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog sfd = new SaveFileDialog())
            {
                sfd.InitialDirectory = JqlConfigurationManager.Manager.DefaultOpenFileDirectory;
                sfd.Filter = "军旗布局文件(*.jql)|*.jql";

                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    JqlInfo.Save(sfd.FileName);
                    OpenedFileName = sfd.FileName;
                    LayoutChanged = false;
                    UndoList.Clear();
                    RedoList.Clear();
                    MessageBox.Show("保存成功", "保存文件", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }
        private void mnuRandom_Click(object sender, EventArgs e)
        {
            if (LayoutChanged)
            {
                DialogResult dlgRst = MessageBox.Show("是否保存当前修改的布局？", "布局保存", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dlgRst == DialogResult.Cancel) return;
                if (dlgRst == DialogResult.Yes)
                {
                    if (SaveJqlFile(ref OpenedFileName) == DialogResult.OK)
                    {
                        MessageBox.Show("保存成功", "保存文件", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
            }

            CreateNewRandomLayout();
        }
        private void mnuFlip_Click(object sender, EventArgs e)
        {
            JqlChessmanCollection chessmanColl = JqlInfo.ChessmanCollection;
            chessmanColl.Flip();
            UndoList.Push(new JqlChessmanSwap());

            pbxChessboard.Invalidate();
        }
        private void mnuCopy_Click(object sender, EventArgs e)
        {
            CopyImageToClipboard();
        }
        private void mnuChessmanColor_Click(object sender, EventArgs e)
        {
            string chessmanColor = (sender as ToolStripMenuItem).Tag.ToString();
            SetColorRadioSelected(chessmanColor);
            ChangeChessmanColor(chessmanColor);
        }
        private void mnuHelp_Click(object sender, EventArgs e)
        {
            using (FrmHelp frmHelp = new FrmHelp())
            {
                frmHelp.ShowDialog(this);
            }
        }
        private void mnuAbout_Click(object sender, EventArgs e)
        {
            using (FrmAbout frmAbout = new FrmAbout())
            {
                frmAbout.ShowDialog(this);
            }
        }
    }
}
